package cn.iocoder.yudao.module.scrm.service.customer;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.scrm.annotations.EncryptPhoneNumbers;
import cn.iocoder.yudao.module.scrm.controller.admin.clue.vo.ClueImportExcelRespVO;
import cn.iocoder.yudao.module.scrm.controller.admin.clue.vo.ClueImportExcelVO;
import cn.iocoder.yudao.module.scrm.controller.admin.customer.vo.*;
import cn.iocoder.yudao.module.scrm.controller.admin.customer.vo.customerrepository.DeptUseScopeByTypeReqVo;
import cn.iocoder.yudao.module.scrm.controller.admin.customer.vo.customerrepository.DeptUseScopeByTypeRespVo;
import cn.iocoder.yudao.module.scrm.dal.dataobject.clue.ClueDO;
import cn.iocoder.yudao.module.scrm.dal.dataobject.customer.CustomerDO;
import cn.iocoder.yudao.module.scrm.dal.dataobject.customer.CustomerPoolConfigDO;
import cn.iocoder.yudao.module.scrm.dal.dataobject.customer.customerrepository.CustomerRepositoryDO;
import cn.iocoder.yudao.module.scrm.dal.dataobject.customer.customerrepository.CustomerRepositoryRuleDO;
import cn.iocoder.yudao.module.scrm.dal.dataobject.customerassistants.CustomerAssistantsDO;
import cn.iocoder.yudao.module.scrm.dal.mysql.customer.CustomerMapper;
import cn.iocoder.yudao.module.scrm.dal.mysql.customer.customerrepository.CustomerRepositoryRuleMapper;
import cn.iocoder.yudao.module.scrm.dal.mysql.customerassistants.CustomerAssistantsMapper;
import cn.iocoder.yudao.module.scrm.enums.common.CrmBizTypeEnum;
import cn.iocoder.yudao.module.scrm.enums.permission.CrmPermissionLevelEnum;
import cn.iocoder.yudao.module.scrm.framework.permission.core.annotations.CrmPermission;
import cn.iocoder.yudao.module.scrm.service.customer.customerrepository.CustomerRepositoryService;
import cn.iocoder.yudao.module.scrm.util.PhoneNumberUtil;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptSimpleDTO;
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.mzt.logapi.context.LogRecordContext;
import com.mzt.logapi.service.impl.DiffParseFunction;
import com.mzt.logapi.starter.annotation.LogRecord;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.scrm.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.scrm.enums.LogRecordConstants.*;
import static java.util.Collections.singletonList;

/**
 * SCRM客户 Service 实现类
 *
 * @author admin
 */
@Service
@Validated
@Slf4j
public class CustomerServiceImpl implements CustomerService {

    @Resource
    private CustomerMapper customerMapper;

    @Resource
    private AdminUserApi adminUserApi;

    @Resource
    private CustomerAssistantsMapper customerAssistantsMapper;

    @Resource
    @Lazy
    private CustomerPoolConfigService customerPoolConfigService;

    @Resource
    private DeptApi deptApi;

    @Resource
    private PermissionApi permissionApi;

    @Resource
    private CustomerRepositoryRuleMapper customerRepositoryRuleMapper;

    @Autowired
    @Lazy//延迟加载第一次使用时在加载
    private CustomerService customerService;



    @Override
    @LogRecord(type = SCRM_CUSTOMER_TYPE, subType = SCRM_CUSTOMER_CREATE_SUB_TYPE, bizNo = "{{#customer.id}}",
            success = SCRM_CUSTOMER_CREATE_SUCCESS)
    public Long createCustomer(CustomerSaveReqVO createReqVO) {
        Long loginUserId = getLoginUserId();
        //校验私库规则
        checkCapacityRuleByCreate(loginUserId,1);
        // 插入
        CustomerDO customer = BeanUtils.toBean(createReqVO, CustomerDO.class);
        //1. 获得系统用户数据
        //负责人默认为当前创建人-登陆人
        customer.setOwnerUserId(loginUserId);
        customer.setOwnerTime(LocalDateTime.now());
        customerMapper.insert(customer);
        LogRecordContext.putVariable("customer", customer);
        // 返回
        return customer.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @LogRecord(type = SCRM_CUSTOMER_TYPE, subType = SCRM_CUSTOMER_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
            success = SCRM_CUSTOMER_UPDATE_SUCCESS)
    public void updateCustomer(CustomerSaveReqVO updateReqVO) {
        Assert.notNull(updateReqVO.getId(), "客户编号不能为空");
        //updateReqVO.setOwnerUserId(null);  // 更新的时候，要把 updateReqVO 负责人设置为空，避免修改
        // 1. 校验存在
        CustomerDO oldCustomer = validateCustomerExists(updateReqVO.getId());
        updateReqVO.setPhone(PhoneNumberUtil.isUpdatePhoneNumber(updateReqVO.getPhone(), oldCustomer.getPhone()));
        // 更新
        CustomerDO updateObj = BeanUtils.toBean(updateReqVO, CustomerDO.class);
        customerMapper.updateById(updateObj);

        // 3. 记录操作日志上下文
        LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldCustomer, CustomerSaveReqVO.class));
        LogRecordContext.putVariable("customerName", oldCustomer.getName());
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        if (loginUser != null) {
            AdminUserRespDTO adminUserRespDTO = adminUserApi.getUserNotCheckDataPermission(loginUser.getId()).getCheckedData();
            LogRecordContext.putVariable("userName", adminUserRespDTO.getNickname());
        }
    }

    @Override
    @LogRecord(type = SCRM_CUSTOMER_TYPE, subType = SCRM_CUSTOMER_UPDATE_DEAL_STATUS_SUB_TYPE, bizNo = "{{#id}}",
            success = SCRM_CUSTOMER_UPDATE_DEAL_STATUS_SUCCESS)
    public void updateCustomerDealStatus(Long id, Boolean dealStatus) {
        // 1.1 校验存在
        CustomerDO customer = validateCustomerExists(id);
        // 1.2 校验是否重复操作
        if (Objects.equals(customer.getDealStatus(), dealStatus)) {
            throw exception(CUSTOMER_UPDATE_DEAL_STATUS_FAIL);
        }

        // 2. 更新客户的成交状态
        customerMapper.updateById(new CustomerDO().setId(id).setDealStatus(dealStatus));

        // 3. 记录操作日志上下文
        LogRecordContext.putVariable("customerName", customer.getName());
        LogRecordContext.putVariable("dealStatus", dealStatus);
    }

    @Override
    public void deleteCustomer(Long id) {
        // 校验存在
        validateCustomerExists(id);
        // 删除
        customerMapper.deleteById(id);
    }

    private CustomerDO validateCustomerExists(Long id) {
        CustomerDO customerDO = customerMapper.selectById(id);
        if (customerDO == null) {
            throw exception(CUSTOMER_NOT_EXISTS);
        }
        return customerDO;
    }

    @Override
    public CustomerDO getCustomer(Long id) {
        return customerMapper.selectById(id);
    }

    @Override
    @EncryptPhoneNumbers
    public PageResult<CustomerDO> getCustomerPage(CustomerPageReqVO pageReqVO) {
        if(pageReqVO.getAssistants() != null) {
            List<CustomerAssistantsDO> selectList = customerAssistantsMapper.selectList(CustomerAssistantsDO::getUserId, pageReqVO.getAssistants());
            if(CollectionUtils.isEmpty(selectList)) {
                return PageResult.empty();
            }
            pageReqVO.setIds(selectList.stream().map(CustomerAssistantsDO::getCustomerId).collect(Collectors.toList()));
        }
        return customerMapper.selectPage(pageReqVO);
    }

    @Override
    @LogRecord(type = SCRM_CUSTOMER_TYPE, subType = SCRM_CUSTOMER_FOLLOW_UP_SUB_TYPE, bizNo = "{{#id}}",
            success = SCRM_CUSTOMER_FOLLOW_UP_SUCCESS)
//    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.WRITE)
    public void updateCustomerFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent) {
        // 1.1 校验存在
        CustomerDO customer = validateCustomerExists(id);

        // 2. 更新客户的跟进信息
        customerMapper.updateById(new CustomerDO().setId(id).setFollowUpStatus(true).setContactNextTime(contactNextTime)
                .setContactLastTime(LocalDateTime.now()).setContactLastContent(contactLastContent));

        // 3. 记录操作日志上下文
        LogRecordContext.putVariable("customerName", customer.getName());
    }

    @Override
    public List<CustomerDO> getCustomerList(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return Collections.emptyList();
        }
        return customerMapper.selectBatchIds(ids);
    }

    @Override
    public void validateCustomer(Long id) {
        validateCustomerExists(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void transferCustomer(TransferCustomerReqVO reqVO) {


        // 1.1 校验客户是否存在
        if (reqVO.getUserId() == null) {
            // 获取用户ID并使用Optional包装
            Optional<Long> idOptional = Optional.ofNullable(adminUserApi.getUserByWxId(reqVO.getWxId()).getData().getId());

            // 判断ID是否存在并处理
            if (idOptional.isPresent()) {
                Long id = idOptional.get();
                adminUserApi.validateUser(id);
                reqVO.setUserId(id);

                //转移的时候校验改用户的私库容量是否超过了上线
                checkCapacityRule(reqVO.getUserId(),reqVO.getIds().size());
            }
        }
        for (Long id : reqVO.getIds()) {
            getSelf().transferCustomerDetail(id,reqVO);
        }
    }

    @Override
    @LogRecord(type = SCRM_CUSTOMER_TYPE, subType = SCRM_CUSTOMER_TRANSFER_SUB_TYPE, bizNo = "{{#id}}",
            success = SCRM_CUSTOMER_TRANSFER_SUCCESS)
    public void transferCustomerDetail(Long id, TransferCustomerReqVO reqVO){
        CustomerDO customer = validateCustomerExists(id);
        // 2.2 转移后重新设置负责人
        customerMapper.updateById(new CustomerDO().setId(id)
                .setOwnerUserId(reqVO.getUserId()).setOwnerTime(LocalDateTime.now())
                .setRemark(reqVO.getRemark()));

        // 3. 记录转移日志
        LogRecordContext.putVariable("customer", customer);
        LogRecordContext.putVariable("newUserId", reqVO.getUserId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @LogRecord(type = SCRM_CUSTOMER_TYPE, subType = SCRM_CUSTOMER_POOL_SUB_TYPE, bizNo = "{{#id}}",
            success = SCRM_CUSTOMER_POOL_SUCCESS)
//    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
    public void putCustomerPool(Long id) {
        // 1. 校验存在
        CustomerDO customer = customerMapper.selectById(id);
        if (customer == null) {
            throw exception(CUSTOMER_NOT_EXISTS);
        }
        // 1.2. 校验是否为公海数据
        validateCustomerOwnerExists(customer, true);
        // 1.3. 校验客户是否锁定
        validateCustomerIsLocked(customer, true);

        // 2. 客户放入公海
        getSelf().putCustomerPool(customer);

        // 记录操作日志上下文
        LogRecordContext.putVariable("customerName", customer.getName());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void receiveCustomer(List<Long> ids, Long ownerUserId, Boolean isReceive) {
        //转移的时候校验改用户的私库容量是否超过了上线
        checkCapacityRuleByReceive(ownerUserId,ids.size());

        // 1.1 校验存在
        List<CustomerDO> customers = customerMapper.selectBatchIds(ids);
        if (customers.size() != ids.size()) {
            throw exception(CUSTOMER_NOT_EXISTS);
        }
        // 1.2 校验负责人是否存在
        adminUserApi.validateUserList(singletonList(ownerUserId));
        // 1.3 校验状态
        customers.forEach(customer -> {
            // 校验是否已有负责人
            validateCustomerOwnerExists(customer, false);
            // 校验是否锁定
            validateCustomerIsLocked(customer, false);
            // 校验成交状态
            validateCustomerDeal(customer);
        });

        // 2. 领取公海数据
        List<CustomerDO> updateCustomers = new ArrayList<>();

        customers.forEach(customer -> {
            // 2.1. 设置负责人
            updateCustomers.add(new CustomerDO().setId(customer.getId())
                    .setOwnerUserId(ownerUserId).setOwnerTime(LocalDateTime.now()));
        });
        // 2.2 更新客户负责人
        customerMapper.updateBatch(updateCustomers);


        // 3. 记录操作日志
        AdminUserRespDTO user = null;
        if (Boolean.FALSE.equals(isReceive)) {
            user = adminUserApi.getUserNotCheckDataPermission(ownerUserId).getCheckedData();
        }
        for (CustomerDO customer : customers) {
            getSelf().receiveCustomerLog(customer, user == null ? null : user.getNickname());
        }
    }

    @Override
    public int autoPutCustomerPool() {
        CustomerPoolConfigDO poolConfig = customerPoolConfigService.getCustomerPoolConfig();
        if (poolConfig == null || !poolConfig.getEnabled()) {
            return 0;
        }
        // 1. 获得需要放到的客户列表
        List<CustomerDO> customerList = customerMapper.selectListByAutoPool(poolConfig);
        // 2. 逐个放入公海
        int count = 0;
        for (CustomerDO customer : customerList) {
            try {
                getSelf().putCustomerPool(customer);
                count++;
            } catch (Throwable e) {
                log.error("[autoPutCustomerPool][客户({}) 放入公海异常]", customer.getId(), e);
            }
        }
        return count;
    }

    /**
     * 批量导入客户或者公海 Excel
     * @param importList 数据
     * @param updateSupport 是否支持更新
     * @param clientOrPool 是否是公海
     * @return 响应
     */
    @Override
    @Transactional(rollbackFor = Exception.class) // 添加事务，异常则回滚所有导入
    public CustomerImportExcelRespVO importCustomerList(List<CustomerImportExcelVO> importList, Boolean updateSupport, Boolean clientOrPool) {
        //获取登陆用户信息
        Long loginUserId = getLoginUserId();
        AdminUserRespDTO userRespDTO = adminUserApi.getUserNotCheckDataPermission(loginUserId).getCheckedData();
        String userName= userRespDTO.getNickname();
        DeptRespDTO deptRespDTO=null;
        if (userRespDTO.getDeptId() != null){
            deptRespDTO = deptApi.getDeptNotCheckDataPermission(userRespDTO.getDeptId()).getCheckedData();
        }
        // 校验非空
        if (CollUtil.isEmpty(importList)) {
            throw exception(CUSTOMER_IMPORT_NOT_IS_EMPTY);
        }
        // 客户导入的响应初始化
        CustomerImportExcelRespVO respVO = CustomerImportExcelRespVO.builder().createClueNames(new ArrayList<>())
                .updateClueNames(new ArrayList<>()).failureClueNames(new LinkedHashMap<>()).numsCount(new LinkedHashMap<>()).build();

        for (int i = 0; i < importList.size(); i++) {
            CustomerImportExcelVO customerImportExcelVO = importList.get(i);
            try {
                // 校验，姓名和手机号码为空就抛错误
                validateClueForCreate(customerImportExcelVO);
            } catch (ServiceException ex) {
                respVO.getFailureClueNames().put("第"+i+"行", ex.getMessage());
                continue;
            }

            //通过手机号数据中是否已经存在
            CustomerDO existCustomer = customerMapper.selectOne(CustomerDO::getPhone,customerImportExcelVO.getPhone());
            if (existCustomer==null){
                //不存在就新增
                // 1.1 插入客户信息
                CustomerDO customer = BeanUtils.toBean(customerImportExcelVO, CustomerDO.class);
                // 如果导入的是客户数据就设置负责人为当前登陆id-和设置时间
                if (clientOrPool){
                    customer.setOwnerUserId(loginUserId).setOwnerTime(LocalDateTime.now());
                }else{
                    customer.setOwnerUserId(null);
                }
                int inserted = customerMapper.insert(customer);
                //记录结果
                respVO.getCreateClueNames().add("导入手机号为："+customer.getPhone()+"的客户成功");
                //记录操作日志
                getSelf().importInsertCustomerLog(customerImportExcelVO,userName,customer);
            }else {
                //存在的话允许更新就跟新，不允许更新就抛出错误
                if (!updateSupport) {
                    respVO.getFailureClueNames().put(customerImportExcelVO.getPhone(),
                            StrUtil.format(CUSTOMER_NAME_EXISTS.getMsg(), customerImportExcelVO.getPhone()));
                    continue;
                }
                // 2.1 更新客户信息
                CustomerDO updateCustomer = BeanUtils.toBean(customerImportExcelVO, CustomerDO.class)
                        .setId(existCustomer.getId());
                if (clientOrPool){
                    updateCustomer.setOwnerUserId(loginUserId).setOwnerTime(LocalDateTime.now());
                }else{
                    updateCustomer.setOwnerUserId(null);
                }
                int updated = customerMapper.updateById(updateCustomer);
                respVO.getUpdateClueNames().add("导入时自动修改手机号为："+existCustomer.getPhone()+"的客户的数据成功");
                // 2.2 记录操作日志
                //clueImportExcelVO.setId(existClue.getId());
                getSelf().importUpdateCustomerLog(customerImportExcelVO,userName,existCustomer, Math.toIntExact(existCustomer.getId()));
            }
        }
        //记录导入结果
        respVO.getNumsCount().put("sumCount",respVO.getUpdateClueNames().size()+respVO.getCreateClueNames().size()+respVO.getFailureClueNames().size());
        respVO.getNumsCount().put("succeedCount",respVO.getUpdateClueNames().size()+respVO.getCreateClueNames().size());
        respVO.getNumsCount().put("failingCount",respVO.getFailureClueNames().size());

        return respVO;
    }


    /**
     * 批量导入客户或者公海 Excel
     * @param importList 数据
     * @return 响应
     */
    @Override
    @Transactional(rollbackFor = Exception.class) // 添加事务，异常则回滚所有导入
    public CustomerImportExcelRespVO importCustomerListNotCanUpdate(List<CustomerImportExcelVO> importList) {
        // 0. 非空校验，确保导入的数据不为空
        validateImportCustomer(importList);

        // 1. 校验数据有效性，返回包含成功与失败的结果-客户导入的响应初始化
        CustomerImportExcelRespVO resultMap = validateCustomerForCreate(importList);

        // 2. 获取当前登录用户信息及其部门信息
        AdminUserRespDTO userRespDTO = adminUserApi.getUserNotCheckDataPermission(getLoginUserId()).getCheckedData();
        String userName = userRespDTO.getNickname();

        // 3. 转换导入的 Excel 数据并设置默认值
        List<CustomerDO> customersToInsert = BeanUtils.toBean(importList, CustomerDO.class, customer -> {
            // 设置默认值
            customer.setOwnerUserId(getLoginUserId()).setOwnerTime(LocalDateTime.now());
        });

        // 记录导入数据总数
        resultMap.getNumsCount().put("sumCount", customersToInsert.size());
        // 4. 批量插入数据并记录日志结果
        insertCustomersAndLog( importList,resultMap, customersToInsert, userName);

        // 5. 返回导入结果
        return recordImportResultsNotCanUpdate(resultMap);
    }

    /**
     * 计算和设置导入结果的统计信息
     * @param respVO 结果响应对象
     */
    private CustomerImportExcelRespVO recordImportResultsNotCanUpdate(CustomerImportExcelRespVO respVO) {
        // 计算创建、更新和失败的记录数
        int createCount = respVO.getCreateClueNames().size();
        int failCount = respVO.getFailureClueNames().size();
        respVO.getNumsCount().putIfAbsent("failingCount", failCount);
        respVO.getNumsCount().put("succeedCount", createCount);

        return respVO;
    }

    private void insertCustomersAndLog(List<CustomerImportExcelVO> importCustomers, CustomerImportExcelRespVO resultMap, List<CustomerDO> customersToInsert, String userName) {
        // 批量插入数据并记录日志
        if (!customersToInsert.isEmpty() && resultMap.getFailureClueNames().isEmpty()) {  // 确保数据非空且无失败信息
            boolean insertResult = customerMapper.insertBatch(customersToInsert);  // 执行批量插入
            if (insertResult) {
                // 插入成功，记录日志
                for (int i = 0; i < customersToInsert.size(); i++) {
                    getSelf().importInsertCustomerLog(importCustomers.get(i),userName, customersToInsert.get(i));  // 记录每条线索的导入日志
                    resultMap.getCreateClueNames().add(String.format("导入手机号为 %s 的客户成功", customersToInsert.get(i).getPhone()));  // 记录成功的线索信息
                }
            }
        }
    }

    private void validateImportCustomer(List<CustomerImportExcelVO> importCustomers) {
        // 校验导入数据是否为空
        Optional.ofNullable(importCustomers)
                .filter(customer -> !customer.isEmpty())
                .orElseThrow(() -> exception(CUSTOMER_IMPORT_NOT_IS_EMPTY));  // 异常抛出
    }

    /**
     * 批量-校验导入的数据中姓名和手机号不能为空
     *
     * @param importCustomers 导入的数据
     */
    @Transactional(rollbackFor = Exception.class)
    public CustomerImportExcelRespVO validateCustomerForCreate(List<CustomerImportExcelVO> importCustomers) {


        // 0.1. 初始化返回响应对象
        CustomerImportExcelRespVO respVO = CustomerImportExcelRespVO.builder()
                .createClueNames(new ArrayList<>())
                .updateClueNames(new ArrayList<>())
                .failureClueNames(new LinkedHashMap<>())
                .numsCount(new LinkedHashMap<>())
                .build();
        //校验负责人的私库容量是否超过上限
        try {
            customerService.checkCapacityRule(getLoginUserId(),importCustomers.size());
        }catch (Exception ex){
            respVO.getFailureClueNames().put("私库校验：","由于您的私库容量已达到上限导入失败");
            respVO.getNumsCount().put("failingCount", importCustomers.size());
        }

        // 1. 收集所有手机号码
        Set<String> phoneNumbers = importCustomers.stream()
                .map(CustomerImportExcelVO::getPhone)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());

        // 2. 批量查询数据库中已有的手机号码
        LambdaQueryWrapper<CustomerDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(CustomerDO::getPhone, phoneNumbers);

        Map<String, CustomerDO> existingCustomers = customerMapper.selectList(queryWrapper)
                .stream()
                .collect(Collectors.toMap(
                        CustomerDO::getPhone,  // 使用 phone 作为键
                        customer -> customer,      // 使用 customerDO 作为值
                        (existing, replacement) -> existing // 如果有重复键，保留第一个 customerDO 对象
                ));

        // 3. 处理每一条导入记录
        for (int i = 0; i < importCustomers.size(); i++) {
            CustomerImportExcelVO customerImportExcelVO = importCustomers.get(i);
            String phone = customerImportExcelVO.getPhone();  // 提取 phone，减少多次调用

            try {
                // 校验，姓名和手机号码为空则抛出异常
                validateClueForCreate(customerImportExcelVO);

                // 检查手机号是否已存在
                CustomerDO existCustomer = existingCustomers.get(phone);
                if (existCustomer != null) {
                    // 提取已存在客户信息
                    handleExistingCustomer(existCustomer, respVO, phone, i);
                }
            } catch (ServiceException ex) {
                // 记录失败的条目及其错误信息
                respVO.getFailureClueNames().put(String.format("Excel第%d行报错了：", i + 1), ex.getMessage());
            }
        }
        return respVO;
    }



    private void handleExistingCustomer(CustomerDO existCustomer, CustomerImportExcelRespVO respVO, String phone, int rowIndex) {
        Long userId = existCustomer.getOwnerUserId();
        if (userId != null) { // 存在于客户中
            // 提取管理员信息
            String nickname = adminUserApi.getUserNotCheckDataPermission(userId).getCheckedData().getNickname();
            if (nickname != null && !nickname.isEmpty()) {
                // 处理日期格式化
                LocalDateTime ownerTime = existCustomer.getOwnerTime();
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
                String formattedDate = ownerTime.format(formatter);
                // 抛出异常
                throw exception(CUSTOMER_IMPORT_PHONE_EXIST_BY_OTHER_PEOPLE, phone, formattedDate, nickname);
            }
        } else { // 存在于客户中（没有 userId）
            // 抛出异常
            throw exception(CUSTOMER_IMPORT_PHONE_EXIST_BY_OTHER_PEOPLE_PUBLIC, existCustomer.getPhone());
        }
    }


    @Override
    @LogRecord(type = SCRM_CUSTOMER_TYPE, subType = SCRM_CUSTOMER_LOCK_SUB_TYPE, bizNo = "{{#lockReqVO.id}}",
            success = SCRM_CUSTOMER_LOCK_SUCCESS)
    public void lockCustomer(CustomerLockReqVO lockReqVO,  Long userId) {
        // 1.1 校验当前客户是否存在
        CustomerDO customer = validateCustomerExists(lockReqVO.getId());
        // 1.2 校验当前是否重复操作锁定/解锁状态
        if (customer.getLockStatus().equals(lockReqVO.getLockStatus())) {
            throw exception(customer.getLockStatus() ? CUSTOMER_LOCK_FAIL_IS_LOCK : CUSTOMER_UNLOCK_FAIL_IS_UNLOCK);
        }
        // 1.3 校验锁定上限和权限| 校验是否有解锁权限
        validateCustomerExceedLockLimit(userId,customer.getOwnerUserId(),lockReqVO.getLockStatus());

        // 2. 更新锁定状态
        customerMapper.updateById(BeanUtils.toBean(lockReqVO, CustomerDO.class));

        // 3. 记录操作日志上下文
        // tips: 因为这里使用的是老的状态所以记录时反着记录，也就是 lockStatus 为 true 那么就是解锁反之为锁定
        LogRecordContext.putVariable("customer", customer);
    }

    @Override
    public void autoUnlockCustomer(CustomerAutoUnlockVO lockReqVO) {
        // 根据请求类型选择不同的处理逻辑
        switch (lockReqVO.getType()) {
            case 1://规则
                handleCustomerRule(lockReqVO);
                break;
            default:
                throw new IllegalArgumentException("Invalid type from DeptUseScopeByTypeReqVo");
        }

    }


    /**
     * 转移客户的时候校验私库容量
     * @param userId 当前登陆用户
     * @param addCount  上传的数量
     */
    @Override
    public void checkCapacityRule(Long userId, int addCount) {
        // 如果是超级管理员不校验
        if (Boolean.TRUE.equals(permissionApi.roleIsSuperAdmin(userId).getCheckedData())) {
            return;
        }

        // 获取用户部门并校验私库容量规则
        Optional.ofNullable(adminUserApi.getUserNotCheckDataPermission(userId).getCheckedData())
                .map(AdminUserRespDTO::getDeptId)//找不到不会执行下一步操作
                .map(customerRepositoryRuleMapper::getRepositoryRuleByDeptId)
                .ifPresent(ruleDO -> {
                    Integer maxCount = ruleDO.getCustomerCount(); // 规则中允许的最大数量
                    // 查询当前登录人为客户负责人的数量
                    Long count = customerMapper.selectCustomerCountByOwnerUserIds(userId);
                    // 如果现有数量加上新添加的数量超出规则的最大数量，则抛出异常
                    if (count + addCount > maxCount) {
                        throw exception(CUSTOMER_REPOSITORY_COUNT_LIMIT);
                    }
                });
    }

    /**
     * 公海领取客户的时候进行校验
     * @param userId 当前登陆用户
     * @param addCount  上传的数量
     */
    @Override
    public void checkCapacityRuleByReceive(Long userId, int addCount) {
        // 如果是超级管理员不校验
        if (Boolean.TRUE.equals(permissionApi.roleIsSuperAdmin(userId).getCheckedData())) {
            return;
        }

        // 获取用户部门并校验私库容量规则
        Optional.ofNullable(adminUserApi.getUserNotCheckDataPermission(userId).getCheckedData())
                .map(AdminUserRespDTO::getDeptId)//找不到不会执行下一步操作
                .map(customerRepositoryRuleMapper::getRepositoryRuleByDeptId)
                .ifPresent(ruleDO -> {
                    Integer maxCount = ruleDO.getCustomerCount(); // 规则中允许的最大数量
                    // 查询当前登录人为客户负责人的数量
                    Long count = customerMapper.selectCustomerCountByOwnerUserIds(userId);
                    // 如果现有数量加上新添加的数量超出规则的最大数量，则抛出异常
                    if (count + addCount > maxCount) {
                        throw exception(CUSTOMER_REPOSITORY_COUNT_LIMIT_BY_RECEIVE);
                    }
                });
    }

    /**
     * 添加客户的时候校验私库容量
     * @param userId 当前登陆用户
     * @param addCount  上传的数量
     */
    @Override
    public void checkCapacityRuleByCreate(Long userId, int addCount) {
        // 如果是超级管理员不校验
        if (Boolean.TRUE.equals(permissionApi.roleIsSuperAdmin(userId).getCheckedData())) {
            return;
        }

        // 获取用户部门并校验私库容量规则
        Optional.ofNullable(adminUserApi.getUserNotCheckDataPermission(userId).getCheckedData())
                .map(AdminUserRespDTO::getDeptId)//找不到不会执行下一步操作
                .map(customerRepositoryRuleMapper::getRepositoryRuleByDeptId)
                .ifPresent(ruleDO -> {
                    Integer maxCount = ruleDO.getCustomerCount(); // 规则中允许的最大数量
                    // 查询当前登录人为客户负责人的数量
                    Long count = customerMapper.selectCustomerCountByOwnerUserIds(userId);
                    // 如果现有数量加上新添加的数量超出规则的最大数量，则抛出异常
                    if (count + addCount > maxCount) {
                        throw exception(CUSTOMER_REPOSITORY_COUNT_LIMIT_BY_CREATE);
                    }
                });
    }



    private void handleCustomerRule(CustomerAutoUnlockVO vo) {
        // 根据操作类型执行不同的逻辑
        switch (vo.getOperationType()) {
            case 0: // 新增规则
                handleAddCustomerRule(vo);
                break;
            case 1: // 删除规则
                handleDeleteCustomerRule(vo);
                break;
            case 2: // 修改规则
                handleUpdateCustomerRule(vo);
                break;
            case 3: // 删除客户库的时候删除多个规则
                handleDeleteCustomerRuleList(vo);
                break;
            default:
                throw new IllegalArgumentException("Invalid operationType from CustomerAutoUnlockVO for handleCustomerRule");
        }
    }

    private void handleAddCustomerRule(CustomerAutoUnlockVO vo) {
        //新增客户库，选择的部门下面的人的客户直接全部解锁
        unlockCustomersByDeptIds(vo.getNewDeptIds());
    }

    /**
     * 处理删除规则的逻辑
     * @param vo 请求参数
     */
    private void handleDeleteCustomerRule(CustomerAutoUnlockVO vo) {
        CustomerRepositoryRuleDO ruleDO = customerRepositoryRuleMapper.selectById(vo.getRepositoryRuleId());
        List<Long> deptIds = ruleDO.getDeptIds();
        //如果改规则下面的部门范围不为空就获取出来
        //通过规则获取到，选择部门的范围，通过部门获取到所有人员的id,
        unlockCustomersByDeptIds(deptIds);
    }

    private void handleDeleteCustomerRuleList(CustomerAutoUnlockVO vo) {
        // 获取所有规则中的部门 ID，并解锁这些部门下所有人员负责的客户
        List<Long> allDeptIds = customerRepositoryRuleMapper.selectListByCustomerRepositoryId(vo.getRepositoryId()).stream()
                .map(CustomerRepositoryRuleDO::getDeptIds)   // 提取每条规则的部门 ID 列表
                .flatMap(List::stream)                       // 将多个列表合并为一个流
                .collect(Collectors.toList());               // 收集成一个完整的列表
        // 解锁所有部门下的客户
        unlockCustomersByDeptIds(allDeptIds);
    }



    /**
     * 处理修改规则的逻辑
     *
     * @param vo 请求参数
     */
    private void handleUpdateCustomerRule(CustomerAutoUnlockVO vo) {
        //修改规则的逻辑
        //TODO 选择新的部门 全部等于 旧的部门   且新的锁定小于旧的锁定，就解锁改规则下所有部门的客户
        //TODO 选择新的部门 多于 旧的部门   多于的那部分，全部解锁，  等于得那部分，再去判断新的锁定数量是否小于旧的锁定数量，小于的话就，全部解锁之前的部门下面的客户
        //TODO 选择新的部门 少于 旧的部门   少掉了的那部分不用管， 等于的那部分，，再去判断新的锁定数量是否小于旧的锁定数量，小于的话就，全部解锁之前的部门下面的客户

        // 获取当前规则
        CustomerRepositoryRuleDO ruleDO = customerRepositoryRuleMapper.selectById(vo.getRepositoryRuleId());
        List<Long> oldDeptIds = ruleDO.getDeptIds();  // 旧部门 ID 列表
        List<Long> newDeptIds = vo.getNewDeptIds();   // 新部门 ID 列表

        // 1. 选择新的部门如果大于旧的部门，找出新增的部门并解锁其下客户
        List<Long> addedDeptIds = newDeptIds.stream()
                .filter(deptId -> !oldDeptIds.contains(deptId))  // 过滤出新增的部门
                .collect(Collectors.toList());

        if (!addedDeptIds.isEmpty()) {
            // 解锁新增部门下的所有客户
            unlockCustomersByDeptIds(addedDeptIds);
        }

        // 2. 新的部门等于旧的部门，且新的锁定数小于旧的锁定数，解锁该规则下所有部门的客户
        if (new HashSet<>(newDeptIds).equals(new HashSet<>(oldDeptIds))) {
            if (vo.getNewCustomerCountLock() < ruleDO.getCustomerCountLock()) {
                unlockCustomersByDeptIds(oldDeptIds);  // 解锁所有旧部门下的客户
            }
        }

        // 3. 新的部门少于旧的部门，解锁等于的部分，如果新的锁定数小于旧的锁定数
        List<Long> commonDeptIds = newDeptIds.stream()
                .filter(oldDeptIds::contains)  // 过滤出等于的部门
                .collect(Collectors.toList());

        if (!commonDeptIds.isEmpty() && vo.getNewCustomerCountLock() < ruleDO.getCustomerCountLock()) {
            unlockCustomersByDeptIds(commonDeptIds);  // 解锁等于的部门下的客户
        }
    }

    /**
     * 根据部门 ID 列表解锁相关客户。
     *
     * 该方法首先通过部门 ID 列表获取对应的人员 ID，接着查询这些人员负责的客户，
     * 最后将这些客户的锁定状态设置为解锁并批量更新客户数据。
     *
     * @param deptIds 部门 ID 列表
     */
    private void unlockCustomersByDeptIds(List<Long> deptIds) {
        // 检查传入的部门 ID 列表是否为空，避免空列表或 null 引发不必要的查询
        if (deptIds != null && !deptIds.isEmpty()) {
            // 通过 adminUserApi 获取部门下的人员列表，并提取人员的 ID 列表
            List<Long> ownerUserIds = Optional.ofNullable(adminUserApi.getUserListByDeptIds(deptIds).getCheckedData())
                    .map(userList -> userList.stream()
                            .map(AdminUserRespDTO::getId) // 提取人员 ID
                            .collect(Collectors.toList()))
                    .orElse(Collections.emptyList()); // 如果为空，则返回空列表

            // 检查提取的人员 ID 列表是否非空
            if (!ownerUserIds.isEmpty()) {
                // 通过人员 ID 查询相关客户
                List<CustomerDO> customerDOList = customerMapper.selectCustomerByOwnerUserIds(ownerUserIds);

                // 检查查询到的客户列表是否为空，确保有客户需要解锁
                if (customerDOList != null && !customerDOList.isEmpty()) {
                    // 对每个客户设置解锁状态
                    customerDOList.forEach(customer -> customer.setLockStatus(false));

                    // 批量更新客户状态
                    customerMapper.updateBatch(customerDOList); // MyBatis-Plus 的批量更新方法
                }
            }
        }
    }


    /**
     * 校验导入的数据中姓名和手机号不能为空
     * @param importCustomer 导入数据
     */
    private void validateClueForCreate(CustomerImportExcelVO importCustomer) {
        if (StrUtil.isEmptyIfStr(importCustomer.getPhone()) && StrUtil.isEmptyIfStr(importCustomer.getName())) {
            throw exception(CLUE_IMPORT_PHONE_NAME_NOT_IS_EMPTY);
        }
        // 校验客户姓名和手机号不能为空
        if (StrUtil.isEmptyIfStr(importCustomer.getName())) {
            throw exception(CLUE_IMPORT_NAME_NOT_IS_EMPTY);
        }
        if (StrUtil.isEmptyIfStr(importCustomer.getPhone())) {
            throw exception(CLUE_IMPORT_PHONE_NOT_IS_EMPTY);
        }
    }

    /**
     * 记录导入客户时新增的操作日志
     */
    @LogRecord(type = SCRM_CUSTOMER_TYPE, subType = SCRM_CUSTOMER_IMPORT_CREATE_SUB_TYPE, bizNo = "{{#customerId}}",
            success = SCRM_CUSTOMER_IMPORT_CREATE_SUCCESS)
    public void importInsertCustomerLog(CustomerImportExcelVO customerImportExcelVO,String userName,CustomerDO newCustomer) {
        LogRecordContext.putVariable("userName", userName);
        LogRecordContext.putVariable("customerName", newCustomer.getName());
        LogRecordContext.putVariable("customerId", newCustomer.getId());
    }

    /**
     * 记录导入客户时修改的操作日志-
     *
     * @param
     */
    @LogRecord(type = SCRM_CUSTOMER_TYPE, subType = SCRM_CLUE_IMPORT_UPDATE_SUB_TYPE, bizNo = "{{#customerId}}",
            success = SCRM_CUSTOMER_IMPORT_UPDATE_SUCCESS)
    public void importUpdateCustomerLog(CustomerImportExcelVO customerImportExcelVO,String userName,CustomerDO oldCustomer,Integer customerId) {
        LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldCustomer, CustomerImportExcelVO.class));
        LogRecordContext.putVariable("userName", userName);
        LogRecordContext.putVariable("customerName", oldCustomer.getName());

    }



    @LogRecord(type = SCRM_CUSTOMER_TYPE, subType = SCRM_CUSTOMER_RECEIVE_SUB_TYPE, bizNo = "{{#customer.id}}",
            success = SCRM_CUSTOMER_RECEIVE_SUCCESS)
    public void receiveCustomerLog(CustomerDO customer, String ownerUserName) {
        // 记录操作日志上下文
        LogRecordContext.putVariable("customer", customer);
        LogRecordContext.putVariable("ownerUserName", ownerUserName);
    }

    private void validateCustomerDeal(CustomerDO customer) {
        if (Boolean.TRUE.equals(customer.getDealStatus())) {
            throw exception(CUSTOMER_ALREADY_DEAL);
        }
    }

    @Transactional(rollbackFor = Exception.class) // 需要 protected 修饰，因为需要在事务中调用
    public void putCustomerPool(CustomerDO customer) {
        // 1. 设置负责人为 NULL
        int updateOwnerUserIncr = customerMapper.updateOwnerUserIdById(customer.getId(), null);
        // 按照客户ID删除协助人数据
        customerAssistantsMapper.delete(CustomerAssistantsDO::getCustomerId, customer.getId());
        if (updateOwnerUserIncr == 0) {
            throw exception(CUSTOMER_UPDATE_OWNER_USER_FAIL);
        }
    }

    private void validateCustomerIsLocked(CustomerDO customer, Boolean pool) {
        if (customer.getLockStatus()) {
            throw exception(pool ? CUSTOMER_LOCKED_PUT_POOL_FAIL : CUSTOMER_LOCKED, customer.getName());
        }
    }

    private void validateCustomerOwnerExists(CustomerDO customer, Boolean pool) {
        if (customer == null) { // 防御一下
            throw exception(CUSTOMER_NOT_EXISTS);
        }
        // 校验是否为公海数据
        if (pool && customer.getOwnerUserId() == null) {
            throw exception(CUSTOMER_IN_POOL, customer.getName());
        }
        // 负责人已存在
        if (!pool && customer.getOwnerUserId() != null) {
            throw exception(CUSTOMER_OWNER_EXISTS, customer.getName());
        }
    }

    /**
     * 校验用户锁定的客户数量，是否到达上限
     *
     * @param userId 用户编号
     */
    private void validateCustomerExceedLockLimit(Long userId,Long ownerUserId,Boolean lockStatus) {
        //TODO 根据用户id查询部门id，根据部门id去查询客户库的规则，取最大的锁定客户数，
        // 如果没有规则就是没有限制，如果有多个部门就是取最大的值，\超级管理员不用管
        //TODO 除了超级管理员其它人只能锁定解锁自己的数据
        // 没有部门就直接返回没有限制
        Long deptId = adminUserApi.getUser(userId).getCheckedData().getDeptId();
        if (deptId==null){
            return;
        }
        if (!userId.equals(ownerUserId) && !permissionApi.roleIsSuperAdmin(userId).getCheckedData()){
            throw exception(lockStatus ? CUSTOMER_LOCK_FAILING : CUSTOMER_UNLOCK_FAILING );
        }
        CustomerRepositoryRuleDO rule = customerRepositoryRuleMapper.getRepositoryRuleByDeptId(deptId);
        if (rule==null){
            return;
        }
        if (lockStatus) {//锁定操作
            Long lockCount = customerMapper.selectCountByLockStatusAndOwnerUserId(true, userId);
            Integer maxCount = rule.getCustomerCountLock();
            assert maxCount != null;
            if (lockCount >= maxCount) {
                throw exception(CUSTOMER_LOCK_EXCEED_LIMIT);
            }
        }
    }

    private CustomerServiceImpl getSelf() {
        return SpringUtil.getBean(getClass());
    }


}